package com.luo.chengrui.framework.sadatapermission.core.aop;

import com.luo.chengrui.framework.sadatapermission.core.annotation.SaDataPermission;
import lombok.Getter;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodClassKey;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * {@link SaDataPermission} 注解的拦截器
 * 1. 在执行方法前，将 @SaDataPermission 注解入栈
 * 2. 在执行方法后，将 @SaDataPermission 注解出栈
 *
 * @author 芋道源码
 */
@SaDataPermission
public class DataPermissionAnnotationInterceptor implements MethodInterceptor {

    /**
     * SaDataPermission 空对象，用于方法无 {@link SaDataPermission} 注解时，使用 DATA_PERMISSION_NULL 进行占位
     */
    static final SaDataPermission DATA_PERMISSION_NULL = DataPermissionAnnotationInterceptor.class.getAnnotation(SaDataPermission.class);

    @Getter
    private final Map<MethodClassKey, SaDataPermission> dataPermissionCache = new ConcurrentHashMap<>();

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // 入栈
        SaDataPermission saDataPermission = this.findAnnotation(methodInvocation);
        if (saDataPermission != null) {
            methodInvocation.getArguments();
            DataPermissionContextHolder.add(saDataPermission);
        }
        try {
            // 执行逻辑
            return methodInvocation.proceed();
        } finally {
            // 出栈
            if (saDataPermission != null) {
                DataPermissionContextHolder.remove();
            }
        }
    }

    private SaDataPermission findAnnotation(MethodInvocation methodInvocation) {
        // 1. 从缓存中获取
        Method method = methodInvocation.getMethod();
        Object targetObject = methodInvocation.getThis();
        Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass();
        MethodClassKey methodClassKey = new MethodClassKey(method, clazz);
        SaDataPermission SaDataPermission = dataPermissionCache.get(methodClassKey);
        if (SaDataPermission != null) {
            return SaDataPermission != DATA_PERMISSION_NULL ? SaDataPermission : null;
        }

        // 2.1 从方法中获取
        SaDataPermission = AnnotationUtils.findAnnotation(method, com.luo.chengrui.framework.sadatapermission.core.annotation.SaDataPermission.class);
        // 2.2 从类上获取
        if (SaDataPermission == null) {
            SaDataPermission = AnnotationUtils.findAnnotation(clazz, com.luo.chengrui.framework.sadatapermission.core.annotation.SaDataPermission.class);
        }
        // 2.3 添加到缓存中
        dataPermissionCache.put(methodClassKey, SaDataPermission != null ? SaDataPermission : DATA_PERMISSION_NULL);
        return SaDataPermission;
    }

}
